Popular Searches
Popular Course Categories
Popular Courses

Creating Reusable Buttons, Cards, and Input Components

Creating Reusable Buttons, Cards, and Input Components

UI Libraries, Themes, and Advanced Interface Design

Creating Reusable Buttons, Cards, and Input Components in React Native

Course: React Native Training

Topic: Creating Reusable Buttons, Cards, and Input Components

Course Link: React Native Training at JustAcademy

Demo Registration: Register for a Course Demo

JustAcademy Website: Visit JustAcademy


1. Introduction to Reusable Components

React Native allows developers to build mobile applications for Android and iOS using JavaScript or TypeScript and React. As applications grow, the same interface elements often appear on multiple screens. Buttons may be used to submit forms, cards may display products or user profiles, and input fields may collect email addresses, passwords, or search queries.

Writing each of these elements separately can lead to duplicated code, inconsistent styling, and difficult maintenance. Reusable components solve this problem by allowing developers to create an interface element once and use it wherever it is needed.

For example, an application may use the same primary button on its login, registration, checkout, and settings screens. If the button's color or padding needs to change, developers can update the shared component instead of editing every screen individually.

1.1 What Is a Reusable Component?

A reusable component is a self-contained part of the user interface that can be used in different places. It accepts properties, known as props, to customize its content, appearance, and behavior.

Common examples include:

  • Reusable buttons: Submit, Cancel, Save, Delete, and Continue actions.
  • Reusable cards: Product cards, profile cards, course cards, and dashboard cards.
  • Reusable input fields: Email, password, phone number, search, and other form fields.
  • Reusable status components: Loading indicators, badges, error messages, and success messages.

1.2 Benefits of Reusable Components

  • Code reusability: Write the component once and use it across multiple screens.
  • Consistency: Maintain a common design for similar interface elements.
  • Maintainability: Make changes in one place rather than updating repeated implementations.
  • Readability: Keep screen components focused on layout and application logic.
  • Customization: Use props to change labels, colors, content, and behavior.
  • Testing: Test shared UI behavior independently.
  • Scalability: Add new screens without rebuilding common interface elements.
  • Accessibility: Apply accessible labels, roles, and interaction states consistently.

2. Understanding Components, Props, and State

Before creating reusable buttons, cards, and inputs, it is important to understand three React concepts: components, props, and state.

2.1 Components

A component is a JavaScript function that returns JSX describing the interface. Component names should begin with an uppercase letter.

import React from 'react';
import { View, Text } from 'react-native';

function WelcomeMessage() {
  return (
    <View>
      <Text>Welcome to React Native!</Text>
    </View>
  );
}

export default WelcomeMessage;

The WelcomeMessage component can be imported and rendered on any screen in the application.

2.2 Props

Props allow a parent component to pass information to a child component. They are useful when the same component must display different content or behave differently in different situations.

function Greeting({ name }) {
  return (
    <Text>Hello, {name}!</Text>
  );
}

// Usage:
// <Greeting name="Aarav" />
// <Greeting name="Diya" />

The component remains the same, but its displayed message changes according to the supplied name prop.

2.3 State

State stores information that can change while the application is running. It is commonly used for input values, loading indicators, selected options, and toggles.

import React, { useState } from 'react';
import { TextInput, Text } from 'react-native';

function NameInput() {
  const [name, setName] = useState('');

  return (
    <>
      <TextInput
        value={name}
        onChangeText={setName}
        placeholder="Enter your name"
      />
      <Text>Your name: {name}</Text>
    </>
  );
}

When the user types into the field, setName updates the state and React renders the updated value.

2.4 Props vs. State

Feature Props State
Purpose Pass data or configuration into a component Store data that changes over time
Updated by The parent component The component through its state setter
Examples Button title, card description, placeholder Input value, loading status, selected tab
Direct mutation Should not be mutated by the receiving component Should be updated through the appropriate state setter

3. Project Structure for Reusable Components

Organizing shared components in a dedicated folder makes a React Native project easier to understand and maintain.

MyReactNativeApp/
  App.js
  src/
    components/
      PrimaryButton.js
      CustomCard.js
      CustomInput.js
    screens/
      LoginScreen.js
      HomeScreen.js
    theme/
      colors.js
      spacing.js

This structure is an example rather than a strict requirement. Small projects may use a simpler layout, while larger applications may organize components by feature or domain.

3.1 Responsibilities of Each File

  • PrimaryButton.js defines a reusable button.
  • CustomCard.js defines a reusable content container.
  • CustomInput.js defines a reusable input field.
  • LoginScreen.js composes the components into a login form.
  • HomeScreen.js uses cards and buttons to display application content.
  • colors.js stores shared color values.
  • spacing.js stores reusable spacing values.

4. Creating a Reusable Button Component

A button is an interactive component that lets users perform an action. Common examples include logging in, submitting a form, saving information, navigating to another screen, or confirming a selection.

React Native provides built-in options such as Button and Pressable. A reusable custom button built with Pressable offers greater control over layout, styling, loading indicators, and interaction states.

4.1 Features of a Good Reusable Button

  • A configurable label.
  • An onPress callback for handling the action.
  • Support for disabled and loading states.
  • Consistent padding, color, and border radius.
  • Accessible role and state information.
  • Optional variants such as primary, secondary, and danger.

4.2 Basic Reusable Button

// src/components/PrimaryButton.js
import React from 'react';
import {
  Pressable,
  Text,
  StyleSheet
} from 'react-native';

export default function PrimaryButton({
  title,
  onPress,
  disabled = false
}) {
  return (
    <Pressable
      onPress={onPress}
      disabled={disabled}
      accessibilityRole="button"
      accessibilityState={{ disabled }}
      style={[
        styles.button,
        disabled && styles.disabled
      ]}
    >
      <Text style={styles.label}>{title}</Text>
    </Pressable>
  );
}

const styles = StyleSheet.create({
  button: {
    backgroundColor: '#2563eb',
    paddingVertical: 14,
    paddingHorizontal: 20,
    borderRadius: 8,
    alignItems: 'center',
    justifyContent: 'center'
  },
  label: {
    color: '#ffffff',
    fontSize: 16,
    fontWeight: '600'
  },
  disabled: {
    opacity: 0.5
  }
});

4.3 Understanding the Button Code

  • title is the text displayed inside the button.
  • onPress is a function executed when the button is pressed.
  • disabled determines whether the button can be activated.
  • Pressable handles touch interactions and supports press-state styling.
  • accessibilityRole="button" identifies the element as a button to accessibility services.
  • accessibilityState communicates whether the button is disabled.
  • StyleSheet.create() keeps styling organized and reusable.

4.4 Using the Reusable Button

import React from 'react';
import { View, Alert } from 'react-native';
import PrimaryButton from './src/components/PrimaryButton';

export default function App() {
  const handleSubmit = () => {
    Alert.alert('Success', 'Your form has been submitted.');
  };

  return (
    <View style={{ padding: 20 }}>
      <PrimaryButton
        title="Submit"
        onPress={handleSubmit}
      />
    </View>
  );
}

The same button can be reused with different labels and callbacks.

<PrimaryButton
  title="Save Changes"
  onPress={handleSave}
/>

<PrimaryButton
  title="Continue"
  onPress={handleContinue}
/>

<PrimaryButton
  title="Delete Account"
  onPress={handleDelete}
  disabled={!canDelete}
/>

4.5 Adding Button Variants

Button variants let developers use a shared component for different action types while maintaining a consistent design.

import React from 'react';
import { Pressable, Text, StyleSheet } from 'react-native';

const variants = {
  primary: {
    backgroundColor: '#2563eb',
    color: '#ffffff'
  },
  secondary: {
    backgroundColor: '#e2e8f0',
    color: '#0f172a'
  },
  danger: {
    backgroundColor: '#dc2626',
    color: '#ffffff'
  }
};

export default function ActionButton({
  title,
  onPress,
  variant = 'primary',
  disabled = false
}) {
  const selectedVariant = variants[variant] || variants.primary;

  return (
    <Pressable
      onPress={onPress}
      disabled={disabled}
      accessibilityRole="button"
      accessibilityState={{ disabled }}
      style={[
        styles.button,
        { backgroundColor: selectedVariant.backgroundColor },
        disabled && styles.disabled
      ]}
    >
      <Text style={[
        styles.label,
        { color: selectedVariant.color }
      ]}>
        {title}
      </Text>
    </Pressable>
  );
}

const styles = StyleSheet.create({
  button: {
    paddingVertical: 13,
    paddingHorizontal: 18,
    borderRadius: 8,
    alignItems: 'center'
  },
  label: {
    fontSize: 16,
    fontWeight: '600'
  },
  disabled: {
    opacity: 0.5
  }
});

Example usage:

<ActionButton title="Sign In" variant="primary" onPress={handleLogin} />
<ActionButton title="Cancel" variant="secondary" onPress={handleCancel} />
<ActionButton title="Delete" variant="danger" onPress={handleDelete} />

4.6 Adding a Loading State

When a button starts an asynchronous task, such as submitting a login request, a loading indicator can communicate that work is in progress and help prevent repeated submissions.

import React from 'react';
import {
  Pressable,
  Text,
  ActivityIndicator,
  StyleSheet
} from 'react-native';

export default function LoadingButton({
  title,
  onPress,
  loading = false,
  disabled = false
}) {
  const unavailable = loading || disabled;

  return (
    <Pressable
      onPress={onPress}
      disabled={unavailable}
      accessibilityRole="button"
      accessibilityState={{
        disabled: unavailable,
        busy: loading
      }}
      style={[
        styles.button,
        unavailable && styles.disabled
      ]}
    >
      {loading ? (
        <ActivityIndicator color="#ffffff" />
      ) : (
        <Text style={styles.label}>{title}</Text>
      )}
    </Pressable>
  );
}

const styles = StyleSheet.create({
  button: {
    minHeight: 48,
    backgroundColor: '#2563eb',
    borderRadius: 8,
    padding: 14,
    alignItems: 'center',
    justifyContent: 'center'
  },
  label: {
    color: '#ffffff',
    fontWeight: '600'
  },
  disabled: {
    opacity: 0.6
  }
});

Use a state variable in the parent screen to control the loading prop. Always handle rejected requests and reset the loading state when the operation finishes.

5. Creating a Reusable Card Component

A card is a container that groups related information into a visually distinct section. Cards are commonly used for product listings, course details, user profiles, dashboard summaries, notifications, and articles.

5.1 Features of a Reusable Card

  • A title and optional description.
  • Optional image, icon, or badge.
  • Configurable content passed through props or children.
  • Consistent padding, border radius, and background color.
  • Optional action buttons.
  • Accessible and understandable content structure.

5.2 Basic Reusable Card

// src/components/CustomCard.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

export default function CustomCard({
  title,
  description,
  children
}) {
  return (
    <View style={styles.card}>
      {title ? (
        <Text style={styles.title}>{title}</Text>
      ) : null}

      {description ? (
        <Text style={styles.description}>
          {description}
        </Text>
      ) : null}

      {children}
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    backgroundColor: '#ffffff',
    padding: 16,
    borderRadius: 12,
    borderWidth: 1,
    borderColor: '#e2e8f0',
    marginBottom: 16
  },
  title: {
    fontSize: 18,
    fontWeight: '700',
    color: '#0f172a',
    marginBottom: 8
  },
  description: {
    fontSize: 14,
    lineHeight: 21,
    color: '#475569'
  }
});

5.3 Using the Card Component

import React from 'react';
import { View, Text } from 'react-native';
import CustomCard from './src/components/CustomCard';

export default function Dashboard() {
  return (
    <View style={{ padding: 20 }}>
      <CustomCard
        title="Welcome"
        description="Manage your account and review your activity."
      />

      <CustomCard title="Account Summary">
        <Text>Membership: Active</Text>
        <Text>Notifications: 3 unread</Text>
      </CustomCard>
    </View>
  );
}

The children prop makes the card flexible. It allows the parent to place additional components inside the card without changing the card implementation.

5.4 Creating a Product Card

import React from 'react';
import { View, Text, Image, StyleSheet } from 'react-native';
import PrimaryButton from './PrimaryButton';

export default function ProductCard({
  name,
  price,
  imageSource,
  onAddToCart
}) {
  return (
    <View style={styles.card}>
      <Image
        source={imageSource}
        style={styles.image}
        resizeMode="cover"
        accessibilityLabel={name}
      />

      <Text style={styles.name}>{name}</Text>
      <Text style={styles.price}>₹{price}</Text>

      <PrimaryButton
        title="Add to Cart"
        onPress={onAddToCart}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    padding: 14,
    borderRadius: 12,
    borderWidth: 1,
    borderColor: '#e2e8f0',
    backgroundColor: '#ffffff'
  },
  image: {
    width: '100%',
    height: 160,
    borderRadius: 8,
    marginBottom: 12
  },
  name: {
    fontSize: 17,
    fontWeight: '700',
    marginBottom: 6
  },
  price: {
    fontSize: 16,
    color: '#2563eb',
    marginBottom: 12
  }
});

The imageSource prop accepts a valid React Native image source. For a bundled image, pass a static require() expression from the calling code. Remote images require a valid URI and appropriate image dimensions.

5.5 Using a Product Card

<ProductCard
  name="Wireless Headphones"
  price={2499}
  imageSource={require('./assets/headphones.png')}
  onAddToCart={() => console.log('Added to cart')}
/>

5.6 Card Design Best Practices

  • Keep each card focused on one main subject or task.
  • Use consistent padding, typography, and border radius.
  • Keep action buttons clearly separated from descriptive text.
  • Avoid overcrowding cards with too many controls.
  • Use meaningful image descriptions when images convey information.
  • Support variable text lengths and different screen sizes.
  • Make a card interactive only when users should be able to activate the card itself.

6. Creating a Reusable Input Component

Input components allow users to enter information into an application. They are used in login forms, registration screens, search bars, profile editing, feedback forms, and checkout pages.

A reusable input component can standardize labels, placeholders, validation messages, borders, and accessibility properties.

6.1 Features of a Good Input Component

  • A configurable label and placeholder.
  • A value and change handler.
  • Support for different keyboard types.
  • Secure text entry for password fields.
  • Optional error messages and validation styling.
  • Disabled or editable states.
  • Accessibility labels and appropriate input behavior.

6.2 Basic Reusable Input

// src/components/CustomInput.js
import React from 'react';
import {
  View,
  Text,
  TextInput,
  StyleSheet
} from 'react-native';

export default function CustomInput({
  label,
  value,
  onChangeText,
  placeholder,
  secureTextEntry = false,
  keyboardType = 'default',
  error,
  editable = true,
  autoCapitalize = 'sentences'
}) {
  return (
    <View style={styles.container}>
      {label ? (
        <Text style={styles.label}>{label}</Text>
      ) : null}

      <TextInput
        value={value}
        onChangeText={onChangeText}
        placeholder={placeholder}
        secureTextEntry={secureTextEntry}
        keyboardType={keyboardType}
        editable={editable}
        autoCapitalize={autoCapitalize}
        accessibilityLabel={label || placeholder}
        accessibilityState={{ disabled: !editable }}
        style={[
          styles.input,
          error ? styles.inputError : null,
          !editable ? styles.inputDisabled : null
        ]}
      />

      {error ? (
        <Text style={styles.errorText}>{error}</Text>
      ) : null}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    marginBottom: 16
  },
  label: {
    fontSize: 14,
    fontWeight: '600',
    marginBottom: 6,
    color: '#0f172a'
  },
  input: {
    minHeight: 48,
    borderWidth: 1,
    borderColor: '#cbd5e1',
    borderRadius: 8,
    paddingHorizontal: 12,
    paddingVertical: 10,
    fontSize: 16,
    color: '#0f172a'
  },
  inputError: {
    borderColor: '#dc2626'
  },
  inputDisabled: {
    backgroundColor: '#f1f5f9'
  },
  errorText: {
    color: '#dc2626',
    marginTop: 5,
    fontSize: 12
  }
});

6.3 Understanding the Input Code

  • label identifies the purpose of the input field.
  • value contains the current input value.
  • onChangeText receives the updated text when the user types.
  • placeholder displays a hint when the input is empty.
  • secureTextEntry hides sensitive text, such as a password.
  • keyboardType requests an appropriate keyboard layout.
  • error displays a validation message and changes the border style.
  • editable controls whether the field can be edited.

6.4 Using the Input Component

import React, { useState } from 'react';
import { View } from 'react-native';
import CustomInput from './src/components/CustomInput';

export default function RegistrationForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  return (
    <View style={{ padding: 20 }}>
      <CustomInput
        label="Email Address"
        value={email}
        onChangeText={setEmail}
        placeholder="Enter your email"
        keyboardType="email-address"
        autoCapitalize="none"
      />

      <CustomInput
        label="Password"
        value={password}
        onChangeText={setPassword}
        placeholder="Enter your password"
        secureTextEntry
        autoCapitalize="none"
      />
    </View>
  );
}

Each field uses the same component but supplies different props. This keeps the design consistent while allowing different input behavior.

6.5 Using Input Validation

import React, { useState } from 'react';
import { View, Alert } from 'react-native';
import CustomInput from './src/components/CustomInput';
import PrimaryButton from './src/components/PrimaryButton';

export default function EmailForm() {
  const [email, setEmail] = useState('');
  const [submitted, setSubmitted] = useState(false);

  const emailError =
    submitted && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())
      ? 'Enter a valid email address.'
      : undefined;

  const handleSubmit = () => {
    setSubmitted(true);

    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())) {
      return;
    }

    Alert.alert('Success', 'The email address is ready to submit.');
  };

  return (
    <View style={{ padding: 20 }}>
      <CustomInput
        label="Email Address"
        value={email}
        onChangeText={setEmail}
        placeholder="[email protected]"
        keyboardType="email-address"
        autoCapitalize="none"
        error={emailError}
      />

      <PrimaryButton
        title="Continue"
        onPress={handleSubmit}
      />
    </View>
  );
}

This example demonstrates basic client-side validation. Real applications should use validation appropriate to their requirements and also validate important data on the server.

7. Controlled and Uncontrolled Inputs

In React Native, a controlled input receives its current value through a prop and reports changes through a callback. The parent component owns the input state.

7.1 Controlled Input Example

const [username, setUsername] = useState('');

<TextInput
  value={username}
  onChangeText={setUsername}
  placeholder="Enter username"
/>

This pattern is useful when the application needs to validate the input, display its current value elsewhere, clear it programmatically, or submit it to a service.

7.2 Why Controlled Inputs Are Useful

  • The application can inspect and validate values.
  • Fields can be cleared or updated programmatically.
  • Validation errors can be displayed immediately or after submission.
  • Form submission can use the current state values.
  • Input behavior can be coordinated with other controls.

8. Combining Buttons, Cards, and Inputs

The real value of reusable components becomes clear when they are combined to build complete screens. A screen can use an input component to collect information, a card to group content, and a button to trigger an action.

8.1 Example: Login Screen

import React, { useState } from 'react';
import {
  View,
  Text,
  Alert,
  StyleSheet
} from 'react-native';

import CustomCard from './src/components/CustomCard';
import CustomInput from './src/components/CustomInput';
import PrimaryButton from './src/components/PrimaryButton';

export default function LoginScreen() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleLogin = () => {
    if (!email.trim() || !password) {
      Alert.alert(
        'Missing Information',
        'Enter your email address and password.'
      );
      return;
    }

    Alert.alert(
      'Login Submitted',
      'Connect this action to your authentication service.'
    );
  };

  return (
    <View style={styles.container}>
      <CustomCard
        title="Welcome Back"
        description="Sign in to access your account."
      >
        <View style={styles.form}>
          <CustomInput
            label="Email Address"
            value={email}
            onChangeText={setEmail}
            placeholder="Enter your email"
            keyboardType="email-address"
            autoCapitalize="none"
          />

          <CustomInput
            label="Password"
            value={password}
            onChangeText={setPassword}
            placeholder="Enter your password"
            secureTextEntry
            autoCapitalize="none"
          />

          <PrimaryButton
            title="Sign In"
            onPress={handleLogin}
          />

          <Text style={styles.note}>
            Please enter your account details to continue.
          </Text>
        </View>
      </CustomCard>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
    backgroundColor: '#f8fafc'
  },
  form: {
    marginTop: 16
  },
  note: {
    color: '#64748b',
    fontSize: 12,
    marginTop: 12
  }
});

8.2 How the Screen Works

  1. The screen creates state variables for the email and password.
  2. Each CustomInput receives a value and an onChangeText handler.
  3. The user enters their credentials into the fields.
  4. When Sign In is pressed, the handleLogin function checks that the required fields are present.
  5. If information is missing, an alert explains what is required.
  6. If the fields are filled, the example displays a message indicating where authentication should be connected.

This example does not authenticate the user. A production application must connect the form to an appropriate authentication service, handle network errors, and protect sensitive information.

9. Styling and Design Consistency

Reusable components are easier to maintain when their visual properties follow shared design rules. Instead of using unrelated values throughout the application, developers can define shared colors, spacing, and typography.

9.1 Create a Color File

// src/theme/colors.js
const colors = {
  primary: '#2563eb',
  secondary: '#64748b',
  background: '#f8fafc',
  surface: '#ffffff',
  text: '#0f172a',
  border: '#cbd5e1',
  error: '#dc2626',
  success: '#16a34a'
};

export default colors;

9.2 Create a Spacing File

// src/theme/spacing.js
const spacing = {
  xs: 4,
  sm: 8,
  md: 16,
  lg: 24,
  xl: 32
};

export default spacing;

9.3 Advantages of Shared Design Values

  • Buttons, cards, and inputs use consistent colors.
  • Spacing adjustments can be applied centrally.
  • Branding changes become easier to implement.
  • Developers can avoid arbitrary values across different screens.
  • Team members can follow the same design standards.

10. Accessibility Best Practices

Reusable components should be designed with accessibility in mind from the beginning. A component used on many screens can improve accessibility throughout the application when its behavior is implemented correctly.

10.1 Button Accessibility

  • Use accessibilityRole="button" for interactive button controls.
  • Provide a meaningful label when the visible text does not fully describe the action.
  • Communicate disabled and busy states.
  • Use readable text and sufficient color contrast.

10.2 Input Accessibility

  • Provide a visible label or a meaningful accessibility label.
  • Use appropriate keyboard types and secure entry for sensitive fields.
  • Associate validation messages clearly with the relevant input.
  • Do not rely only on red or green colors to communicate errors and success.

10.3 Card Accessibility

  • Keep the content hierarchy logical.
  • Provide useful descriptions for meaningful images.
  • Ensure buttons inside a card can be reached and activated independently.
  • Avoid making the entire card interactive unless that behavior is clear to users.

11. Performance and Maintainability

Reusable components can improve maintainability, but reusability alone does not guarantee better performance. Components should be designed with clear responsibilities and efficient rendering behavior.

11.1 Performance Tips

  • Keep component responsibilities focused and easy to understand.
  • Avoid unnecessary state in components that do not need it.
  • Use stable keys when rendering lists of reusable cards.
  • Profile screens before applying complex performance optimizations.
  • Avoid creating large, complicated components with too many unrelated props.
  • Use FlatList or SectionList for long collections of items.

11.2 Maintainability Tips

  • Use clear component and prop names.
  • Keep styling definitions organized.
  • Provide sensible default prop values where appropriate.
  • Document complex props and expected callback behavior.
  • Keep business logic in the screen, hook, or service layer when it does not belong in the visual component.
  • Test shared components because a change may affect many screens.

12. Common Mistakes and Troubleshooting

Problem Possible Cause Solution
Button does not respond The onPress callback is missing or not passed correctly. Check the callback prop and confirm that the button is not disabled.
Button action runs during rendering The callback was invoked instead of passed as a function. Use onPress={handleSubmit} rather than onPress={handleSubmit()}.
Input text does not change The value is controlled but its state is not updated. Pass a working onChangeText handler that updates the value.
Card content is missing Props or children are not passed or rendered correctly. Check the component call and render the appropriate props and children.
Styles are inconsistent Different components use unrelated style values. Use shared design tokens and common component styles.
Password is visible The secure text entry property is missing. Set secureTextEntry for password inputs.
Validation error does not appear The error prop is not updated or passed correctly. Check the validation condition and pass the resulting message to the input component.
Button can be pressed repeatedly during a request The loading state does not disable the button. Disable the button while the asynchronous operation is running.
Text is clipped on smaller screens Fixed dimensions or insufficient layout flexibility. Review padding, flex behavior, text wrapping, and device sizes.
Shared component changes break other screens A prop or style change affects existing uses. Review all usages, preserve expected behavior, and test important screens.

13. Testing Reusable Components

Reusable components should be tested individually and as part of complete screens. Testing helps confirm that props, callbacks, validation, and interaction states work as expected.

13.1 Button Testing Checklist

  • Verify that the correct title is displayed.
  • Verify that pressing the button invokes the supplied callback.
  • Verify that disabled buttons cannot be activated.
  • Verify that loading indicators appear when loading is true.
  • Verify accessible labels and states.

13.2 Card Testing Checklist

  • Verify that the title and description render correctly.
  • Verify that children appear in the expected location.
  • Check cards with optional content omitted.
  • Test long descriptions and different content lengths.
  • Check layout on different screen sizes.

13.3 Input Testing Checklist

  • Verify that typed text updates the value.
  • Verify that the placeholder appears when the field is empty.
  • Verify secure entry for password fields.
  • Verify validation messages.
  • Verify that non-editable fields cannot be changed.
  • Check keyboard behavior and accessibility labels.

14. Mini Project: Reusable Registration Form

Objective: Build a registration screen by combining reusable buttons, cards, and input components.

14.1 Requirements

  • A registration card with a title and short description.
  • Name, email, and password input fields.
  • Validation for required fields and email format.
  • A reusable Register button.
  • A loading state during form submission.
  • Readable validation messages.
  • Accessible labels and appropriate keyboard behavior.
  • A consistent color and spacing system.

14.2 Suggested Structure

src/
  components/
    PrimaryButton.js
    CustomCard.js
    CustomInput.js
  screens/
    RegistrationScreen.js
  theme/
    colors.js
    spacing.js
App.js

14.3 Implementation Steps

  1. Create the three reusable components.
  2. Define the state variables for the registration form.
  3. Pass each input's value and change handler through props.
  4. Create a validation function for the required fields.
  5. Display validation messages using the input component's error prop.
  6. Use the reusable button to trigger form submission.
  7. Disable the button and display a loading indicator during the request.
  8. Handle success and failure responses.
  9. Test the screen on a target device or emulator.
  10. Check accessibility, layout, and input behavior.

14.4 Expected Outcome

After completing this project, learners should be able to build a form by composing reusable components instead of duplicating UI code. They should also understand how props, state, validation, styling, and event handlers work together.

15. Practical Exercises

  1. Create a reusable button that supports primary, secondary, and danger variants.
  2. Add loading and disabled states to the button.
  3. Create a card component that accepts a title, description, and children.
  4. Build a product card that displays an image, product name, price, and action button.
  5. Create an input component with a label, placeholder, and error message.
  6. Build a controlled email field using useState.
  7. Combine the three components into a login screen.
  8. Use shared color and spacing values across all three components.
  9. Add accessible roles, labels, and disabled states.
  10. Build the registration form mini project and test its behavior.

16. Interview Questions and Answers

Q1. What is a reusable component in React Native?

Answer: A reusable component is a UI element that can be used in multiple places. It accepts props to customize its content, appearance, or behavior.

Q2. What are the benefits of reusable components?

Answer: They reduce duplicated code, improve consistency, simplify maintenance, support testing, and make applications easier to scale.

Q3. What are props?

Answer: Props are values passed from a parent component to a child component to configure its content or behavior.

Q4. What is state in React?

Answer: State stores information that can change over time, such as input values, loading status, or selected options.

Q5. Why might you use Pressable to create a button?

Answer: Pressable supports touch interactions and flexible styling, making it suitable for building custom buttons with different visual and interaction states.

Q6. What is the purpose of the onPress prop?

Answer: The onPress prop supplies a callback that runs when the user activates the interactive component.

Q7. Why should a button support disabled and loading states?

Answer: These states communicate availability and progress, prevent inappropriate interactions, and help reduce repeated submissions during asynchronous operations.

Q8. What is a card component?

Answer: A card groups related information into a visually distinct container, often with a title, description, image, or actions.

Q9. What is the children prop?

Answer: The children prop represents nested content placed between a component's opening and closing tags. It allows reusable containers to accept flexible content.

Q10. What is a controlled input?

Answer: A controlled input receives its value through a prop and reports changes through a callback, with the current value typically stored in React state.

Q11. Why is useState useful for forms?

Answer: useState stores form values and updates the UI when users type or interact with the form.

Q12. How can you show validation errors in a reusable input?

Answer: Pass an error message through a prop and render the message alongside an appropriate error style when the value is invalid.

Q13. How do you make reusable components accessible?

Answer: Provide meaningful labels, appropriate roles and states, sufficient contrast, understandable validation messages, and usable touch targets.

Q14. Why use shared design tokens?

Answer: Design tokens centralize values such as colors and spacing, making the interface more consistent and easier to update.

Q15. Where should business logic be placed?

Answer: Logic related to the screen or application workflow generally belongs in the parent screen, a custom hook, or a service layer. Reusable visual components should focus on presentation and clearly defined interactions.

Q16. How should reusable components be tested?

Answer: Test their rendering, props, callbacks, disabled and loading states, validation behavior, accessibility, and integration within screens.

17. Quick Revision Notes

  • Reusable components reduce repeated interface code.
  • Props configure the appearance and behavior of components.
  • State stores information that changes during interaction.
  • Pressable is useful for building customizable buttons.
  • Cards group related information into a consistent container.
  • Input components can standardize labels, values, placeholders, and validation errors.
  • Controlled inputs use a value and a change handler.
  • Loading and disabled states improve interaction feedback.
  • Shared design tokens help maintain consistent styling.
  • Accessibility should be included in reusable components.
  • Test shared components individually and within complete screens.

18. Final Checklist

  • Understand the role of reusable components.
  • Create a configurable button with callback handling.
  • Support button variants, disabled states, and loading feedback.
  • Create a card that accepts props and nested children.
  • Create a controlled input with labels and validation messages.
  • Combine buttons, cards, and inputs into a functional screen.
  • Use shared colors and spacing values.
  • Implement accessibility labels and states.
  • Test validation, interaction, layout, and error handling.
  • Complete the registration form mini project.

19. Conclusion

Creating reusable buttons, cards, and input components is an important skill in React Native development. These components help developers maintain a consistent design, reduce duplicated code, and build screens more efficiently.

A well-designed reusable button supports clear actions and interaction states. A reusable card organizes related information, while a reusable input standardizes data entry and validation. Props make these components configurable, and React state allows screens to respond to user interactions.

By combining reusable components with shared design tokens, accessibility practices, and systematic testing, developers can build React Native applications that are easier to maintain, expand, and improve.

Continue Learning: Explore React Native Training at JustAcademy

Register for a Demo: Register for a Course Demo

Visit JustAcademy: https://www.justacademy.co/

whatsapp